package com.linxiao.call.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import com.linxiao.call.domain.CrmWebcallFile;
import com.linxiao.call.domain.CrmWebcallFileTel;
import com.linxiao.call.mapper.CrmWebcallFileMapper;
import com.linxiao.call.service.ICrmWebCallService;
import com.linxiao.call.service.ICrmWebcallFileService;
import com.linxiao.call.service.ICrmWebcallFileTelService;
import com.linxiao.call.vo.CrmWebCallVO;
import com.ruoyi.common.constant.CrmFileTaskConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.manager.AdminManager;

/**
 * 语音通知批次Service业务层处理
 * 
 * @author qiqi
 * @date 2022-06-28
 */
@Service
public class CrmWebcallFileServiceImpl implements ICrmWebcallFileService
{
    public Logger logger = LoggerFactory.getLogger(getClass());
	@Resource
	private AdminManager adminManager;
    @Resource
    private CrmWebcallFileMapper crmWebcallFileMapper;
    @Resource
    private ICrmWebcallFileTelService crmWebcallFileTelService;
    @Resource
    private ICrmWebCallService crmWebCallService;

    // 天润语音 单次请求呼叫数
    @Value("${tr.webcall.singleReqCallCount:null}")
    private String singleReqCallCount;

    // 天润语音 单次请求间隔时间 单位 s
    @Value("${tr.webcall.singleReqIntervalTime:null}")
    private String singleReqIntervalTime;
    /**
     * 任务队列  此处使用ArrayBlockingQueue有界队列，防止队列无限膨胀导致内存溢出
     */
    private static final BlockingQueue WORK_QUEUE = new ArrayBlockingQueue(50);

    /**
     * 拒绝策略 CallerRunsPolicy：不在新线程中执行任务，而是由调用者所在的线程来执行
     */
    private static final RejectedExecutionHandler REJECTED_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();

    /**
     * 线程工厂
     */
    private static final ThreadFactory THREAD_FACTORY = new MyThreadFactory();

    /**
     * 线程池核心线程数
     */
    private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();

    /**
     * 线程池最大线程数
     */
    private static final int MAX_POOL_SIZE = Runtime.getRuntime().availableProcessors()+1;


    private static ThreadPoolExecutor poolExecutor = null;

    static {
        poolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                60,
                TimeUnit.SECONDS,
                WORK_QUEUE,
                THREAD_FACTORY,
                REJECTED_HANDLER);
    }

    /**
     * 查询语音通知批次
     * 
     * @param id 语音通知批次主键
     * @return 语音通知批次
     */
    @Override
    public CrmWebcallFile selectCrmWebcallFileById(Long id)
    {
        return crmWebcallFileMapper.selectCrmWebcallFileById(id);
    }

    /**
     * 查询语音通知批次列表
     * 
     * @param crmWebcallFile 语音通知批次
     * @return 语音通知批次
     */
    @Override
    public List<CrmWebcallFile> selectCrmWebcallFileList(CrmWebcallFile crmWebcallFile)
    {
        return crmWebcallFileMapper.selectCrmWebcallFileList(crmWebcallFile);
    }

    /**
     * 新增语音通知批次
     * 
     * @param crmWebcallFile 语音通知批次
     * @return 结果
     */
    @Override
    public int insertCrmWebcallFile(CrmWebcallFile crmWebcallFile)
    {
        crmWebcallFile.setCreateTime(DateUtils.getNowDate());
        try{
            LoginUser loginUser = SecurityUtils.getLoginUser();
            crmWebcallFile.setDeptId(loginUser.getDeptId());
            crmWebcallFile.setUserId(loginUser.getUserId());
        }catch (Exception e){
          logger.error("insertCrmWebcallFile 获取登录用户信息异常 e={}",e);
        }


        return crmWebcallFileMapper.insertCrmWebcallFile(crmWebcallFile);
    }

    /**
     * 修改语音通知批次
     * 
     * @param crmWebcallFile 语音通知批次
     * @return 结果
     */
    @Override
    public int updateCrmWebcallFile(CrmWebcallFile crmWebcallFile)
    {
        crmWebcallFile.setUpdateTime(DateUtils.getNowDate());
        return crmWebcallFileMapper.updateCrmWebcallFile(crmWebcallFile);
    }

    /**
     * 批量删除语音通知批次
     * 
     * @param ids 需要删除的语音通知批次主键
     * @return 结果
     */
    @Override
    public AjaxResult deleteCrmWebcallFileByIds(Long[] ids)
    {
        for (Long id : ids) {
            CrmWebcallFile webcallFile = crmWebcallFileMapper.selectCrmWebcallFileById(id);
            if(CrmFileTaskConstants.RUNING_VALUE.equals(webcallFile.getStatus())){
                StringBuilder builder = new StringBuilder();
                builder.append("批次ID").append("【").append(id).append("】").append("任务处于运行中，删除失败");
                return new AjaxResult(HttpStatus.ERROR, builder.toString());
            }
            int innerCount = crmWebcallFileTelService.countById(id);
            if (innerCount > 0) {
                StringBuilder builder = new StringBuilder();
                builder.append("批次ID").append("【").append(id).append("】").append("下存在批次号码，删除失败");
                return new AjaxResult(HttpStatus.ERROR, builder.toString());
            }
        }
        int count = crmWebcallFileMapper.deleteCrmWebcallFileByIds(ids);
        if (count > 0) {
            return new AjaxResult(HttpStatus.SUCCESS, "删除成功");
        }
        return new AjaxResult(HttpStatus.ERROR, "删除失败");
    }

    /**
     * 删除语音通知批次信息
     * 
     * @param id 语音通知批次主键
     * @return 结果
     */
    @Override
    public int deleteCrmWebcallFileById(Long id)
    {
        return crmWebcallFileMapper.deleteCrmWebcallFileById(id);
    }

    /**
     * 批次任务启动
     * @param id
     * @return
     */
    @Override
    @SuppressWarnings("all")
    public AjaxResult start(Long id) {
        if (id == null || id <= 0) {
            return new AjaxResult(HttpStatus.BAD_REQUEST, "参数[id]不正确");
        }
        // 1、判断批次信息是否存在
        CrmWebcallFile crmWebcallFile = crmWebcallFileMapper.selectCrmWebcallFileById(id);
        if(crmWebcallFile == null){
            return new AjaxResult(HttpStatus.ERROR, "没有获得到批次信息");
        }
        // 2、判断号码 没有号码不能启动
        CrmWebcallFileTel webcallFileTelParam = new CrmWebcallFileTel();
        webcallFileTelParam.setFileId(id);
        List<CrmWebcallFileTel> crmWebcallFileTels = crmWebcallFileTelService.selectCrmWebcallFileTelList(webcallFileTelParam);

        if (StringUtils.isNull(crmWebcallFileTels) || crmWebcallFileTels.size() == 0) {
            return new AjaxResult(HttpStatus.ERROR, "任务中没有号码,请导入号码后启动任务");
        }

        // 3、判断批次是否在运行中
        if(CrmFileTaskConstants.RUNING_VALUE.equals(crmWebcallFile.getStatus())){
            return new AjaxResult(HttpStatus.ERROR, "当前任务在运行中，无法启动");
        }

        // 4、启动线程开始执行呼叫逻辑
        try{
            poolExecutor.execute(() ->{
                List<CrmWebcallFileTel> crmWebcallFileTelList = new ArrayList<>();
                Long startTimeMillis = System.currentTimeMillis();
                long tick = 0;
                int callCount = -1;
                try{//NOSONAR
                    CrmWebcallFile webcallFile = new CrmWebcallFile();
                    webcallFile.setId(id);
                    webcallFile.setStatus(CrmFileTaskConstants.RUNING_VALUE);
                    int count = crmWebcallFileMapper.updateCrmWebcallFile(webcallFile);
                    Long trDeptId = crmWebcallFile.getTrDeptId();
                    CrmWebCallVO crmWebCallVO = null;

                    for (int i = 0; i < crmWebcallFileTels.size(); i++) {
                        callCount ++;
                        CrmWebcallFileTel webcallFileTel = crmWebcallFileTels.get(i);
                        crmWebCallVO = new CrmWebCallVO();
                        crmWebCallVO.setDeptId(trDeptId);
                        // 兼容测试环境
                        if(webcallFileTel.getIvrId() != null){
                            crmWebCallVO.setIvrId(webcallFileTel.getIvrId().intValue());
                        }
                        crmWebCallVO.setRequestUniqueId(UUID.randomUUID().toString().replace("-", ""));
                        // 设置批次ID
                        crmWebCallVO.setFileId(id.intValue());
                        AjaxResult ajaxResult = crmWebCallService.webCall(crmWebCallVO);
                        logger.info("CrmWebcallFileServiceImpl.start 调用天润返回 ajaxResult={}",ajaxResult.get("msg"));

                        // 将数据存储
                        webcallFileTel.setCallStatus(CrmFileTaskConstants.CALL_VALUE);

                        crmWebcallFileTelList.add(webcallFileTel);

                        if (i == crmWebcallFileTels.size() - 1 || ((i != 0) && (i % 100 == 0))) {

                            if (crmWebcallFileTels.size() > 0) {
                                crmWebcallFileTelService.batchUpdateFileTel(crmWebcallFileTelList);
                                crmWebcallFileTelList = new ArrayList<>();
                            }
                        }
                        // 控制请求速度
                        try {//NOSONAR
                            if (callCount != 0 && callCount % Integer.valueOf(singleReqCallCount) == 0) {
                                TimeUnit.SECONDS.sleep(Integer.valueOf(singleReqIntervalTime));
                            }
                        } catch (InterruptedException  e) {
                            logger.error("TimeUnit.SECONDS error e={}",e);
                            Thread.currentThread().interrupt();
                        }
                    }
                    tick = System.currentTimeMillis() - startTimeMillis;

                    // 更新状态为已结束
                    webcallFile.setStatus(CrmFileTaskConstants.END_VALUE);
                    crmWebcallFileMapper.updateCrmWebcallFile(webcallFile);
                }catch (Exception e){//NOSONAR
                    logger.error("CrmWebcallFileServiceImpl.start. 任务启动失败id = {}, error ={}",id,e);
                }finally {
                    logger.info("CrmWebcallFileServiceImpl.start 任务执行总耗时 time = {},fileId ={}",tick,id);
                }
            });


        }catch (Exception e){
            logger.error("CrmWebcallFileServiceImpl.start.poolExecutor 线程异常 error ={}",e);
        }
        // 更新数据库状态变为运行中


        return new AjaxResult(HttpStatus.SUCCESS, "任务启动成功");
    }


    /**
     * 任务取消
     * @param id
     * @return
     */
    @Override
    public AjaxResult cancel(Long id) {
        return null;
    }


    /**
     * 自定义线程工厂
     */
    static class MyThreadFactory implements ThreadFactory {
        private final AtomicInteger threadId = new AtomicInteger();

        /**
         * 设置线程名称
         */
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "tel-thread:" + threadId.getAndIncrement());
        }
    }

    /**
     * 根据线程ID获取到对应的线程
     * @param threadId
     * @return
     */
    public static Thread findThread(long threadId) {

        ThreadGroup group = Thread.currentThread().getThreadGroup(); //NOSONAR

        while(group != null) {

            Thread[] threads = new Thread[(int)(group.activeCount() * 1.2)];

            int count = group.enumerate(threads, true);

            for(int i = 0; i < count; i++) {

                if(threadId == threads[i].getId()) {

                    return threads[i];

                }

            }

            group = group.getParent();

        }

        return null;

    }
}
